#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
独立的密码加密解密工具
实现与Jasypt兼容的PBEWITHHMACSHA512ANDAES_256算法
无需依赖Java环境，纯Python实现

使用方法:
    python backend/src/main/resources/encrypt-password.py
    
然后按提示选择加密、解密或批量操作
"""

import hashlib
import hmac
import os
import base64
import getpass
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

class JasyptCompatibleEncryptor:
    """
    与Jasypt兼容的加密器
    实现PBEWITHHMACSHA512ANDAES_256算法
    兼容Jasypt的确切实现格式
    """
    
    def __init__(self, password="korean-learning-secret-2023", iterations=1000):
        self.password = password.encode('utf-8')
        self.iterations = iterations
        self.algorithm = "PBEWITHHMACSHA512ANDAES_256"
        self.salt_size = 16  # Jasypt使用16字节salt
        self.iv_size = 16    # AES-256需要16字节IV
        self.key_size = 32   # AES-256需要32字节密钥
    
    def _derive_key_and_iv(self, salt, iv):
        """
        使用PBKDF2-HMAC-SHA512派生密钥
        与Jasypt的实现方式完全兼容
        """
        # Jasypt使用PBKDF2-HMAC-SHA512只派生密钥，IV是单独生成的
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA512(),
            length=self.key_size,  # 只派生32字节AES密钥
            salt=salt,
            iterations=self.iterations,
            backend=default_backend()
        )
        key = kdf.derive(self.password)
        return key
    
    def encrypt(self, plaintext):
        """加密文本"""
        if isinstance(plaintext, str):
            plaintext = plaintext.encode('utf-8')
        
        # 生成随机salt和IV（Jasypt分别生成）
        salt = os.urandom(self.salt_size)
        iv = os.urandom(self.iv_size)
        
        # 派生密钥
        key = self._derive_key_and_iv(salt, iv)
        
        # AES-256-CBC加密
        cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
        encryptor = cipher.encryptor()
        
        # PKCS7填充
        padding_length = 16 - (len(plaintext) % 16)
        padded_plaintext = plaintext + bytes([padding_length] * padding_length)
        
        # 加密
        ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize()
        
        # Jasypt格式：salt + IV + ciphertext
        encrypted_data = salt + iv + ciphertext
        
        # Base64编码
        return base64.b64encode(encrypted_data).decode('ascii')
    
    def decrypt(self, encrypted_text):
        """解密文本"""
        try:
            # 移除ENC()包装（如果存在）
            if encrypted_text.startswith("ENC(") and encrypted_text.endswith(")"):
                encrypted_text = encrypted_text[4:-1]
            
            # Base64解码
            encrypted_data = base64.b64decode(encrypted_text.encode('ascii'))
            
            # Jasypt格式解析：salt(16) + IV(16) + ciphertext
            if len(encrypted_data) < (self.salt_size + self.iv_size):
                raise Exception("加密数据格式不正确：数据太短")
            
            salt = encrypted_data[:self.salt_size]
            iv = encrypted_data[self.salt_size:self.salt_size + self.iv_size]
            ciphertext = encrypted_data[self.salt_size + self.iv_size:]
            
            # 派生密钥
            key = self._derive_key_and_iv(salt, iv)
            
            # AES-256-CBC解密
            cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
            decryptor = cipher.decryptor()
            
            # 解密
            padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()
            
            # 移除PKCS7填充
            padding_length = padded_plaintext[-1]
            if padding_length < 1 or padding_length > 16:
                raise Exception("填充数据不正确")
            
            plaintext = padded_plaintext[:-padding_length]
            
            return plaintext.decode('utf-8')
            
        except Exception as e:
            raise Exception(f"解密失败: {str(e)}")

def print_colored(text, color='white'):
    """打印彩色文本"""
    colors = {
        'red': '\033[0;31m',
        'green': '\033[0;32m',
        'yellow': '\033[1;33m',
        'blue': '\033[0;34m',
        'white': '\033[0m'
    }
    print(f"{colors.get(color, colors['white'])}{text}\033[0m")

def main():
    """主函数"""
    print_colored("=== 韩语学习应用独立密码加密工具 ===", 'blue')
    print_colored("纯Python实现，兼容Jasypt PBEWITHHMACSHA512ANDAES_256算法", 'yellow')
    print_colored("无需Java环境和Maven", 'green')
    print()
    
    # 创建加密器实例
    encryptor = JasyptCompatibleEncryptor()
    
    while True:
        print_colored("请选择操作:", 'green')
        print("1. 加密新密码")
        print("2. 解密密码")
        print("3. 批量显示当前所有加密值")
        print("4. 退出")
        print()
        
        choice = input("请输入选择 (1-4): ").strip()
        
        if choice == '1':
            # 加密单个密码
            print()
            password = getpass.getpass("请输入要加密的密码: ")
            if not password:
                print_colored("密码不能为空", 'red')
                continue
                
            print_colored("配置名称说明:", 'blue')
            print("这是在application.yml中使用的环境变量名称")
            print("例如: DB_PASSWORD, MAIL_PASSWORD, JWT_SECRET, API_KEY 等")
            print("如果不确定，可以直接按回车使用默认值")
            print()
            config_name = input("请输入配置名称 (如: NEW_PASSWORD): ").strip()
            if not config_name:
                config_name = "NEW_PASSWORD"
            
            try:
                print_colored("正在加密...", 'yellow')
                encrypted = encryptor.encrypt(password)
                
                print_colored("加密成功!", 'green')
                print(f"原文: {password}")
                print(f"密文: ENC({encrypted})")
                print()
                print_colored("配置格式（复制到application.yml）:", 'yellow')
                print(f"  ${{{config_name}:ENC({encrypted})}}")
                print()
                print_colored("使用说明:", 'blue')
                print(f"1. 将上述配置格式复制到application.yml对应位置")
                print(f"2. 或者设置环境变量: {config_name}={password}")
                print(f"3. 启动应用时使用: -Djasypt.encryptor.password=korean-learning-secret-2023")
                
            except Exception as e:
                print_colored(f"加密失败: {str(e)}", 'red')
            print()
            
        elif choice == '2':
            # 解密密码
            print()
            encrypted_text = input("请输入要解密的密文 (可以包含ENC()): ").strip()
            if not encrypted_text:
                print_colored("密文不能为空", 'red')
                continue
            
            try:
                print_colored("正在解密...", 'yellow')
                decrypted = encryptor.decrypt(encrypted_text)
                
                print_colored("解密成功!", 'green')
                print(f"密文: {encrypted_text}")
                print(f"原文: {decrypted}")
                
            except Exception as e:
                print_colored(f"解密失败: {str(e)}", 'red')
            print()
            
        elif choice == '3':
            # 显示批量加密结果
            print_colored("正在生成当前所有配置的加密值...", 'yellow')
            print()
            
            # 预定义的配置
            configs = [
                ("数据库密码", "123456", "DB_PASSWORD"),
                ("JWT密钥", "korean-learning-jwt-secret-key-2023-for-development", "JWT_SECRET"),
                ("邮箱密码", "your-mail-password", "MAIL_PASSWORD")
            ]
            
            try:
                print_colored("=== 批量加密结果 ===", 'blue')
                print(f"使用密钥: korean-learning-secret-2023")
                print(f"算法: PBEWITHHMACSHA512ANDAES_256")
                print()
                
                for config_name, plaintext, env_key in configs:
                    encrypted = encryptor.encrypt(plaintext)
                    print_colored(f"{config_name}:", 'green')
                    print(f"  原文: {plaintext}")
                    print(f"  密文: ENC({encrypted})")
                    print(f"  配置: ${{{env_key}:ENC({encrypted})}}")
                    print()
                
                print_colored("批量加密完成！", 'green')
                print("启动应用时使用: -Djasypt.encryptor.password=korean-learning-secret-2023")
                
            except Exception as e:
                print_colored(f"批量加密失败: {str(e)}", 'red')
            print()
            
        elif choice == '4':
            print_colored("再见!", 'green')
            break
        else:
            print_colored("无效选择，请重新输入", 'red')
            print()

if __name__ == '__main__':
    # 检查依赖
    try:
        import cryptography
    except ImportError:
        print_colored("错误: 缺少cryptography库", 'red')
        print("请安装: pip install cryptography")
        exit(1)
    
    main() 